package officeserver.office;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.logging.Level;

import officeserver.log_error.AppointmentException;
import officeserver.log_error.FileIOException;
import officeserver.log_error.Log;
import officeserver.log_error.OfficeException;
import officeserver.log_error.UserException;
import officeserver.log_error.FileIOException.REASON;
import officeserver.main.MODULE;
import officeserver.report.Appointment;
import officeserver.users.AccessLevel;
import officeserver.users.PersonalInfo;
import officeserver.users.User;
import officeserver.users.UserList;

/**
 * @author Chris Bayruns This is the Office class.
 * @version $Revision: 1.0 $
 */

public class Office implements OfficeInterface {
    private User userLoggedIn = null;

    private User userSearchedFor = null;
    public static final String SYSTEM_SAVE_NAME = new String("office.dtbs");

    public Office() {

    }

    /**
     * This method attemts to log in.
     * 
     * @param userName
     *            The username to search for and log into
     * @param password
     *            The password to check
     * @return The user object if logged in, null if not
     * @throws UserException
     */
    public boolean userLogin(String userName, String password)
	    throws UserException {
	User result = null;

	result = UserList.searchForUsername(userName);
	if (result.checkPassword(password)) {
	    userLoggedIn = result;
	}

	return userLoggedIn != null;
    }

    /**
     * @author Chris Bayruns
     */
    public void userLogout() {
	userLoggedIn = null;
    }

    /**
     * This checks weather the user is logged in
     * 
     * @return Whether you are logged in
     */
    public boolean isLoggedIn() {
	return userLoggedIn != null;
    }

    /**
     * Check to see if you are logged in
     * 
     * @author Chris Bayruns
     * @throws UserException
     *             Not logged in
     */
    private void checkMyLogin() throws UserException {
	if (!this.isLoggedIn()) {
	    throw (new UserException(Level.INFO,
		    UserException.REASON.NOT_LOGGED_IN, ""));
	}
    }

    /**
     * Checks to see if there is a user searched for
     * 
     * @author Chris Bayruns
     * @throws UserException
     *             No such search
     */
    private void checkMySearch() throws UserException {
	if (userSearchedFor == null) {
	    throw (new UserException(Level.INFO,
		    UserException.REASON.NO_SEARCH_EXISTS, ""));
	}
    }

    /**
     * Check to see if you are admin in
     * 
     * @author Chris Bayruns
     * @throws UserException
     *             Not logged in
     */
    private void checkMyAdmin() throws UserException {
	if (this.userLoggedIn.getAccessLevel() != AccessLevel.ADMIN) {
	    throw (new UserException(Level.INFO,
		    UserException.REASON.NO_ACCESS, "User: "
			    + this.userLoggedIn.getUsername()));
	}
    }

    /**
     * Check to see if you are admin in
     * 
     * @author Chris Bayruns
     * @throws UserException
     *             Not logged in
     */
    private void checkMyNurseDoc() throws UserException {
	if (!(this.userLoggedIn.getAccessLevel() == AccessLevel.NURSE
		|| this.userLoggedIn.getAccessLevel() == AccessLevel.DOCTOR || this.userLoggedIn
		.getAccessLevel() != AccessLevel.ADMIN)) {
	    throw (new UserException(Level.INFO,
		    UserException.REASON.NO_ACCESS, "User: "
			    + this.userLoggedIn.getUsername()));
	}
    }
    
    /**
     * Check to see if you are admin in
     * 
     * @author Chris Bayruns
     * @throws UserException
     *             Not logged in
     */
    private void checkMyNotPatient() throws UserException {
	if (this.userLoggedIn.getAccessLevel() == AccessLevel.PATIENT) {
	    throw (new UserException(Level.INFO,
		    UserException.REASON.NO_ACCESS, "User: "
			    + this.userLoggedIn.getUsername()));
	}
    }

    /**
     * Adds a user
     * 
     * @author Chris Bayruns
     * @param firstName
     *            The first name
     * @param lastName
     *            The last name
     * @param username
     *            The username
     * @param password
     *            The password
     * @param accessLevel
     *            The access level
     * @throws UserException
     */
    public void addUser(String firstName, String lastName, String username,
	    String password, AccessLevel accessLevel) throws UserException {
	checkMyLogin();
	checkMyNotPatient();

	UserList.addUser(firstName, lastName, username, password, accessLevel);
    }

    /**
     * @author Chris Bayruns
     * @return The personal info object associated with the logged in account
     * @throws UserException
     */
    public PersonalInfo getSelfPersonalInfo() throws UserException {
	checkMyLogin();
	return userLoggedIn.getPersonalInfo();
    }

    /**
     * @author Chris Bayruns
     * @return The personal info object associated with the logged in account
     * @throws UserException
     */
    public void setSelfPersonalInfo(PersonalInfo info) throws UserException {
	checkMyLogin();
	userLoggedIn.setPersonalInfo(info);
    }

    /**
     * @author Chris Bayruns
     * @param username
     *            The username to search for
     * @throws UserException
     */
    public void userSearch(String firstName, String lastName)
	    throws UserException {
	checkMyLogin();
	userSearchedFor = UserList.searchForName(firstName, lastName);
    }

    /**
     * @author Chris Bayruns
     * @param username
     *            The username to search for
     * @throws UserException
     */
    public void userSearch(String username) throws UserException {
	checkMyLogin();
	userSearchedFor = UserList.searchForUsername(username);
    }

    /**
     * @author Chris Bayruns
     * @param id
     *            The id to search for
     * @param level
     *            The access level to search for it in
     * @throws UserException
     */
    public void userSearch(int id, AccessLevel level) throws UserException {
	checkMyLogin();
	userSearchedFor = UserList.searchForID(id, level);
    }

    /**
     * @author Chris Bayruns
     * @return The personal info of the user searched for
     * @throws UserException
     */
    public PersonalInfo getSearchPersonalInfo() throws UserException {
	checkMyLogin();
	checkMySearch();
	PersonalInfo result = null;

	result = userSearchedFor.getPersonalInfo();

	return result;
    }

    /**
     * @author Chris Bayruns
     * @param info
     *            Sets the personal info of the searched user
     * @throws UserException
     */
    public void setSearchPersonalInfo(PersonalInfo info) throws UserException {
	checkMyLogin();
	checkMySearch();

	userSearchedFor.setPersonalInfo(info);
    }

    /**
     * Deletes a user
     * 
     * @author Chris Bayruns
     * @throws UserException
     */
    public void deleteSearchUser() throws UserException {
	checkMyLogin();
	checkMySearch();
	checkMyAdmin();
	UserList.delUser(userSearchedFor);
    }

    /**
     * @author Chris Bayruns
     */
    public void clearSearch() {
	userSearchedFor = null;
    }

    /**
     * @author Chris Bayruns
     * @return An arraylist of the patients appointments
     * @throws UserException
     */
    public ArrayList<Appointment> getSelfAppointments() throws UserException {
	checkMyLogin();
	return userLoggedIn.getAppointments();
    }

    /**
     * @author Chris Bayruns
     * @return The appointment arraylist of the searched for user
     * @throws UserException
     */
    public ArrayList<Appointment> getSearchUserAppointments()
	    throws UserException {
	checkMyLogin();
	checkMyNurseDoc();
	checkMySearch();

	return AppointmentList.getAppointmentsFromUser(userSearchedFor);
    }
    
    /**
     * @author Chris Bayruns
     * @return The appointment arraylist of the searched for user
     * @throws UserException
     */
    public Appointment getSearchUserAppointments(int index)
	    throws UserException {
	checkMyLogin();
	checkMyNurseDoc();
	checkMySearch();

	return AppointmentList.getAppointmentsFromUser(userSearchedFor).get(index);
    }

    /**
     * @author Chris Bayruns
     * @param day
     *            The day to get appointments on
     * @return The arraylist of the appointments
     * @throws Exception
     */
    public ArrayList<Appointment> getAppointmentsByDay(int day)
	    throws Exception {
	checkMyLogin();
	checkMyNurseDoc();

	return AppointmentList.getAppointmentsFromDay(day);
    }

    /**
     * @author Chris Bayruns
     * @param delete
     *            The appointment to delete
     * @throws Exception
     */
    public void deleteSearchUserAppointment(Appointment delete)
	    throws Exception {
	checkMyLogin();
	checkMyNurseDoc();
	checkMySearch();

	AppointmentList.removeAppointment(delete);
    }

    /**
     * @author Chris Bayruns
     * @param update
     *            The appointment to update
     * @throws Exception
     */
    public void updateSearchUserAppointment(Appointment update)
	    throws Exception {
	checkMyLogin();
	checkMyNurseDoc();
	checkMySearch();

	AppointmentList.updatePatientAppointment(update);
    }

    /**
     * @author Chris Bayruns
     * @param docID
     *            The doctor you want
     * @param day
     *            The day you want
     * @param timeSlot
     *            The timeslot you want
     * @throws Exception
     */
    public void addSelfAppointment(int docID, int day, int timeSlot)
	    throws Exception {
	checkMyLogin();

	AppointmentList.addAppointment(null, docID, this.userLoggedIn.getId(),
		day, timeSlot);
    }

    /**
     * @author Chris Bayruns
     * @throws UserException
     * @throws AppointmentException
     */
    public void confirmSearchUserAppointment(Appointment confirm)
	    throws UserException, AppointmentException {
	checkMyLogin();
	checkMyNurseDoc();
	checkMySearch();

	AppointmentList.confirmAppointment(confirm);
    }

    /**
     * @author Chris Bayruns
     * @param day
     *            The day to get appointments on
     * @return The arraylist of the appointments
     * @throws UserException
     * @throws Exception
     */
    public ArrayList<Appointment> getWaitingAppointments() throws UserException {
	checkMyLogin();
	checkMyNurseDoc();

	return AppointmentList.getWaitingAppointments();
    }

    /**
     * @author Chris Bayruns
     * @param day
     *            The day to search
     * @return The avaliablity of dat
     * @throws Exception
     */
    public boolean[][] selfCheckCalendar(int day) throws Exception {
	checkMyLogin();

	return Calendar.getDay(day).getAvaliability();
    }

    public void  save() throws FileIOException {
	//checkMyAdmin();
	String fileName = Office.SYSTEM_SAVE_NAME;
	FileOutputStream fileOut = null;
	ObjectOutputStream objectOut = null;
	ObjectOutWrapper files = new ObjectOutWrapper();
	files.setAppointments(AppointmentList.getAppointments());
	files.setUserList(UserList.getUserList());
	files.setCalendar(Calendar.getCalendar());
	files.setAppointmentsWaiting(AppointmentList.getWaitingAppointments());
	try {
	    fileOut = new FileOutputStream(fileName);
	    objectOut = new ObjectOutputStream(fileOut);
	    objectOut.writeObject(files);
	    objectOut.close();
	} catch (IOException e) {
	    throw (new FileIOException(Level.WARNING, REASON.SAVE_FAILURE,
		    "Failure to save to file!"));
	}
    }

    public void load() throws OfficeException {
	//checkMyAdmin();
	String fileName = Office.SYSTEM_SAVE_NAME;
	FileInputStream fileIn = null;
	ObjectInputStream objectIn = null;
	try {
	    fileIn = new FileInputStream(fileName);
	    objectIn = new ObjectInputStream(fileIn);
	    ObjectOutWrapper in = (ObjectOutWrapper) objectIn.readObject();
	    objectIn.close();
	    UserList.setUserList(in.getUserList());
	    AppointmentList.setAppointments(in.getAppointments());
	    AppointmentList.setWaitingAppointments(in.getAppointmentsWaiting());
	    Calendar.setCalendar(in.getCalendar());
	} catch (Exception e) {
	    Log.writeToLog(MODULE.OFFICE, Level.INFO,
		    "Java Database not found.");
	    throw (new OfficeException());
	}
    }

    @Override
    public ArrayList<PersonalInfo> getAllUsers() throws UserException {
	checkMyLogin();
	//checkMyNurseDoc();
	ArrayList<PersonalInfo> result = new ArrayList<PersonalInfo>();
	
	for( User user : UserList.getUserList())
	{
	    result.add(user.getPersonalInfo());
	}
	return result;
    }

    @Override
    public Integer getSearchUserNumAppointments() throws UserException {
	checkMyLogin();
	checkMyNurseDoc();
	checkMySearch();

	return AppointmentList.getAppointmentsFromUser(userSearchedFor).size();
    }

    @Override
    public Integer getSelfUserNumAppointments() throws UserException {
	checkMyLogin();

	return userLoggedIn.getAppointments().size();
    }

}
